package com.fsh.lingsp.common.chat.service.strategy.msg;

import cn.hutool.core.collection.CollectionUtil;
import com.fsh.lingsp.common.chat.dao.MessageDao;
import com.fsh.lingsp.common.chat.domain.entity.Message;
import com.fsh.lingsp.common.chat.domain.entity.msg.MessageExtra;
import com.fsh.lingsp.common.chat.domain.enums.MessageStatusEnum;
import com.fsh.lingsp.common.chat.domain.enums.MessageTypeEnum;
import com.fsh.lingsp.common.chat.domain.vo.request.msg.TextMsgReq;
import com.fsh.lingsp.common.chat.domain.vo.response.msg.TextMsgResp;
import com.fsh.lingsp.common.chat.service.adapter.MessageAdapter;
import com.fsh.lingsp.common.chat.service.cache.MsgCache;
import com.fsh.lingsp.common.common.domain.enums.YseOrNoEnum;
import com.fsh.lingsp.common.common.utils.AssertUtil;
import com.fsh.lingsp.common.common.utils.discover.PrioritizedUrlDiscover;
import com.fsh.lingsp.common.common.utils.discover.domain.UrlInfo;
import com.fsh.lingsp.common.common.utils.sensitive.SensitiveWordBs;
import com.fsh.lingsp.common.user.domain.entity.User;
import com.fsh.lingsp.common.user.domain.enums.RoleEnum;
import com.fsh.lingsp.common.user.service.RoleService;
import com.fsh.lingsp.common.user.service.cache.UserCache;
import com.fsh.lingsp.common.user.service.cache.UserInfoCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 作者：fsh
 * 日期：2024/03/10
 * <p>
 * 描述：
 * Description: 普通文本消息处理器。
 * 实现了 消息回复+跳转 和 @群成员
 */
@Component
public class TextMsgHandler extends AbstractMsgHandler<TextMsgReq> {
    @Autowired
    private MessageDao messageDao;
    @Autowired
    private MsgCache msgCache;
    @Autowired
    private UserCache userCache;
    @Autowired
    private UserInfoCache userInfoCache;
    @Autowired
    private RoleService RoleService;
    @Autowired
    private SensitiveWordBs sensitiveWordBs;

    private static final PrioritizedUrlDiscover URL_TITLE_DISCOVER = new PrioritizedUrlDiscover();

    @Override
    MessageTypeEnum getMsgTypeEnum() {
        return MessageTypeEnum.TEXT;
    }

    /**
     * 检查消息
     *
     * @param body   文本消息入参
     * @param roomId 房间 ID
     * @param uid    当前用户uid
     */
    @Override
    protected void checkMsg(TextMsgReq body, Long roomId, Long uid) {
        // 校验回复消息是否合法
        if (Objects.nonNull(body.getReplyMsgId())) {// 如果回复消息的ID不为null
            // 根据回复消息的ID从数据库中获取回复消息对象
            Message replyMsg = messageDao.getById(body.getReplyMsgId());
            // 校验回复消息对象是否不为空
            AssertUtil.isNotEmpty(replyMsg, "回复消息不存在");
            // 校验回复消息是否属于当前会话
            AssertUtil.equal(replyMsg.getRoomId(), roomId, "只能回复相同会话内的消息");
        }

        // 校验@的用户列表是否合法
        if (CollectionUtil.isNotEmpty(body.getAtUidList())) { // 如果@的用户列表不为空
            // 对@的用户列表进行去重处理
            //前端传入的@用户列表可能会重复，需要去重
            List<Long> atUidList = body.getAtUidList().stream().distinct().collect(Collectors.toList());
            // 批量从用户信息缓存中获取@的用户信息
            Map<Long, User> batch = userInfoCache.getBatch(atUidList);

            // 统计实际存在的用户数量（过滤掉value为null的项）
            //如果@用户不存在，userInfoCache 返回的map中依然存在该key，但是value为null，需要过滤掉再校验
            long batchCount = batch.values().stream().filter(Objects::nonNull).count();

            // 校验实际存在的用户数量与@的用户列表大小是否一致
            AssertUtil.equal((long)atUidList.size(), batchCount, "@用户不存在");

            // 校验是否@了所有人（通常使用0L作为标识）
            boolean atAll = body.getAtUidList().contains(0L);
            if (atAll) {
                // 如果@了所有人，则校验当前用户是否具有聊天管理员权限
                AssertUtil.isTrue(RoleService.hasPower(uid, RoleEnum.CHAT_MANGER), "没有权限");
            }
        }
    }

    /**
     * 定义保存文本消息的方法，传入一个Message消息对象和一个TextMsgReq对象
     *
     * @param msg  Message消息实体
     * @param body 文本消息入参
     */
    @Override
    public void saveMsg(Message msg, TextMsgReq body) {
        // 如果msg的extra字段不为null，则使用它，否则创建一个新的MessageExtra对象
        MessageExtra extra = Optional.ofNullable(msg.getExtra()).orElse(new MessageExtra());

        // 创建一个新的Message对象用于更新数据库
        Message update = new Message();
        // 设置更新对象的id为原msg对象的id
        update.setId(msg.getId());
        // 过滤敏感词后，设置更新对象的内容
        update.setContent(sensitiveWordBs.filter(body.getContent()));
        // 设置更新对象的extra字段
        update.setExtra(extra);

        // 如果有回复消息的ID
        if (Objects.nonNull(body.getReplyMsgId())) {
            // 计算回复消息与当前消息之间的间隔消息数量
            Integer gapCount = messageDao.getGapCount(msg.getRoomId(), body.getReplyMsgId(), msg.getId());
            // 设置更新对象的间隔消息数量
            update.setGapCount(gapCount);
            // 设置更新对象的回复消息ID
            update.setReplyMsgId(body.getReplyMsgId());
        }

        // 解析消息内容中的URL，并构建URL和标题的映射关系
        Map<String, UrlInfo> urlContentMap = URL_TITLE_DISCOVER.getUrlContentMap(body.getContent());
        // 将URL和标题的映射关系设置到extra对象中
        extra.setUrlContentMap(urlContentMap);

        // 如果body中有@的用户列表
        if (CollectionUtil.isNotEmpty(body.getAtUidList())) {
            // 将@的用户列表设置到extra对象中
            extra.setAtUidList(body.getAtUidList());
        }

        // 调用messageDao的updateById方法，根据id更新数据库中的消息记录
        messageDao.updateById(update);
    }

    /**
     * @param msg 味精
     * @return {@link Object }
     */
    @Override
    public Object showMsg(Message msg) {
        TextMsgResp resp = new TextMsgResp();
        resp.setContent(msg.getContent());
        resp.setUrlContentMap(Optional.ofNullable(msg.getExtra()).map(MessageExtra::getUrlContentMap).orElse(null));
        resp.setAtUidList(Optional.ofNullable(msg.getExtra()).map(MessageExtra::getAtUidList).orElse(null));
        //回复消息
        Optional<Message> reply = Optional.ofNullable(msg.getReplyMsgId())
                .map(msgCache::getMsg)
                .filter(a -> Objects.equals(a.getStatus(), MessageStatusEnum.NORMAL.getStatus()));
        if (reply.isPresent()) {
            Message replyMessage = reply.get();
            TextMsgResp.ReplyMsg replyMsgVO = new TextMsgResp.ReplyMsg();
            replyMsgVO.setId(replyMessage.getId());
            replyMsgVO.setUid(replyMessage.getFromUid());
            replyMsgVO.setType(replyMessage.getType());
            //策略  ，回复哪种类型的消息 ，就获取对应的类型，调用它的showReplyMsg
            replyMsgVO.setBody(MsgHandlerFactory.getStrategyNoNull(replyMessage.getType()).showReplyMsg(replyMessage));
            User replyUser = userCache.getUserInfo(replyMessage.getFromUid());
            replyMsgVO.setUsername(replyUser.getName());
            replyMsgVO.setCanCallback(YseOrNoEnum.toStatus(Objects.nonNull(msg.getGapCount()) && msg.getGapCount() <= MessageAdapter.CAN_CALLBACK_GAP_COUNT));
            replyMsgVO.setGapCount(msg.getGapCount());
            resp.setReply(replyMsgVO);
        }
        return resp;
    }

    @Override
    public Object showReplyMsg(Message msg) {
        return msg.getContent();
    }

    @Override
    public String showContactMsg(Message msg) {
        return msg.getContent();
    }
}
